package org.gw.ylc.base.ctrl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import org.gw.ylc.base.data.*;
import org.gw.ylc.base.repositories.CommonRepository;
import org.gw.ylc.base.util.FastJSONConverter;
import org.hibernate.metadata.ClassMetadata;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.convert.support.ConfigurableConversionService;
import org.springframework.stereotype.Controller;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import java.util.*;

@Controller
@RequestMapping("common")
public class CommonCtrl implements InitializingBean {
	@Autowired
	private CommonRepository commonRepository;
	@Autowired
	private ConfigurableConversionService conversionService;
	@Autowired
	private PermissionContext permissionContext;

	@RequestMapping("query")
	@ResponseBody
	public Object query(CommonQuery commonQuery) {
		ClassMetadata metadata = commonRepository.getMetadata(commonQuery.getEntity());
		if (metadata == null) {
			return null;
		}
		EntityPermission permission = permissionContext.getPermission(metadata.getEntityName());
		if (permission == null) {
			return null;
		}
		Class<?> entityType = metadata.getMappedClass();
		StringBuilder hql = new StringBuilder("select ").append(commonQuery.getAlias()).append(" from ");
		hql.append(commonQuery.entity).append(" ");
		hql.append(commonQuery.getAlias());
		List<Object> paramater = new ArrayList<Object>();
		List<JoinQuery> joinQuerys = commonQuery.getJoinQuerys();
		StringBuilder where = new StringBuilder(" where 1=1 ");
		boolean appendWhere = false;
		if (joinQuerys != null && joinQuerys.size() > 0) {
			for (JoinQuery jq : joinQuerys) {
				List<Condition> conditions = jq.getConditions();
				if (conditions != null && conditions.size() > 0) {
					StringBuilder jqWhere = new StringBuilder();
					jqWhere.append(commonQuery.getAlias()).append(".").append(jq.getMasterProperty());
					jqWhere.append(" in (select ").append(jq.getAlias()).append(".").append(jq.getSlaveProperty());
					jqWhere.append(" from ").append(jq.getEntity()).append(" ").append(jq.getAlias());
					jqWhere.append(" where 1=1 ");
					Class<?> jqType = commonRepository.getEntityType(jq.getEntity());
					appendWhere = true;
					for (Condition condition : conditions) {
						fillCondition(jqType, jq.getAlias(), jqWhere, condition, paramater);
					}
					jqWhere.append(")");
					where.append(" and ").append(jqWhere);
				}
				// hql.append(" ").append(jqWhere);
			}
		}

		List<QueryFilter> filters = permission.getFilters();
		if (filters.size() > 0) {
			appendWhere = true;
			for (QueryFilter filter : filters) {
				fillCondition(entityType, commonQuery.getAlias(), where, filter.toCondition(), paramater);
			}
		}
		if (commonQuery.id != null && commonQuery.id.length > 0) {
			appendWhere = true;
			where.append(" and ");
			where.append(commonQuery.getAlias()).append(".").append(commonQuery.idProperty).append(" in ");
			paramater.add(Arrays.asList(commonQuery.id));
			where.append("?").append(paramater.size());
		}
		if (commonQuery.conditions != null && commonQuery.conditions.size() > 0) {
			appendWhere = true;
			for (Condition condition : commonQuery.conditions) {
				fillCondition(entityType, commonQuery.getAlias(), where, condition, paramater);
			}
		}
		if (appendWhere) {
			hql.append(where);
		}
		List<Order> orders = commonQuery.getOrders();
		if (orders != null && orders.size() > 0) {
			hql.append(" order by ");
			for (Order order : orders) {
				hql.append(commonQuery.getAlias()).append(".").append(order.getField()).append(" ")
						.append(StringUtils.isEmpty(order.getType()) ? "asc" : order.getType()).append(",");
			}
			hql.setLength(hql.length() - 1);
		}
		if (commonQuery.page) {
			return commonRepository.page(hql.toString(), paramater.toArray());
		} else {
			if (commonQuery.single) {
				return commonRepository.findOne(hql.toString(), paramater.toArray());
			} else {
				return commonRepository.query(hql.toString(), paramater.toArray());
			}
		}
	}

	@RequestMapping("update")
	@ResponseBody
	public Object update(String entity, String objects) throws InstantiationException, IllegalAccessException {
		ClassMetadata metadata = commonRepository.getMetadata(entity);
		if (metadata == null) {
			return null;
		}
		EntityPermission permission = permissionContext.getPermission(metadata.getEntityName());
		if (permission == null) {
			return null;
		}
		JSONArray array = JSON.parseArray(objects);
		Class<?> entityType = commonRepository.getEntityType(entity);
		for (int i = 0; i < array.size(); i++) {
			JSONObject jsonObject = array.getJSONObject(i);
			Object entityObject = conversionService.convert(jsonObject, entityType);
			String hql = permission.getHql();
			List<Object> parameters = permission.getParameters(entityObject);
			String filters = withFilters(parameters, permission);
			commonRepository.executeUpdate(hql + filters, parameters);
		}
		return "success";
	}

	@RequestMapping("remove")
	@ResponseBody
	public Object remove(String entity, String[] id) throws InstantiationException, IllegalAccessException {
		ClassMetadata metadata = commonRepository.getMetadata(entity);
		if (metadata == null) {
			return null;
		}
		EntityPermission permission = permissionContext.getPermission(metadata.getEntityName());
		if (permission == null || !permission.canDelete()) {
			return null;
		}
		StringBuilder where = new StringBuilder(" where ").append(metadata.getIdentifierPropertyName());
		where.append(" in ?1 ");
		List<Object> paramater = new ArrayList<Object>();
		paramater.add(Arrays.asList(id));
		List<QueryFilter> filters = permission.getFilters();
		if (filters.size() > 0) {
			for (QueryFilter filter : filters) {
				fillCondition(metadata.getMappedClass(), "", where, filter.toCondition(), paramater);
			}
		}
		commonRepository.executeUpdate("delete from " + entity + where, paramater);
		return "success";
	}

	@RequestMapping("save")
	@ResponseBody
	public Object save(String entity, String objects) throws InstantiationException, IllegalAccessException {
		JSONArray array = JSON.parseArray(objects);
		Class<?> entityType = commonRepository.getEntityType(entity);
		EntityPermission permission = permissionContext.getPermission(entityType.getName());
		if (permission == null || !permission.canCreate()) {
			return null;
		}
		List<Object> result = new ArrayList<>();
		for (int i = 0; i < array.size(); i++) {
			JSONObject jsonObject = array.getJSONObject(i);
			Object save = commonRepository.save(conversionService.convert(jsonObject, entityType));
			result.add(save);
		}
		return result;
	}

	private Set<String> nullableOperator = new HashSet<String>(Arrays.asList("is", "is not"));

	public void fillCondition(Class<?> entityType, String alias, StringBuilder hql, Condition condition,
			List<Object> paramater) {
		// TODO 权限校验

		hql.append(" ").append(condition.logical).append(" ");
		if (condition.subConditions != null && condition.subConditions.size() > 0) {
			hql.append("( 1=1 ");
			for (Condition sub : condition.subConditions) {
				fillCondition(entityType, alias, hql, sub, paramater);
			}
			hql.append(")");
		} else {
			if (StringUtils.hasText(alias)) {
				hql.append(alias).append(".");
			}
			hql.append(condition.field).append(" ").append(condition.operator);
			Object param = null;
			if (condition.value != null && condition.value.length > 0) {
				for (int i = 0; i < condition.value.length; i++) {
					if (condition.value[i] != null)
						condition.value[i] = conversionService.convert(condition.value[i],
								getType(entityType, condition.field));
				}
				param = condition.value.length == 1 ? condition.value[0] : Arrays.asList(condition.value);
			}
			// param = conversionService.convert(param, getType(entityType,
			// condition.field));
			if (param == null && nullableOperator.contains(condition.operator)) {
				hql.append(" null");
			} else {
				paramater.add(param);
				hql.append(" ?").append(paramater.size());
			}

		}
	}

	public String withFilters(List<Object> paramList, EntityPermission permission) {
		StringBuilder filter = new StringBuilder();
		String alias = permission.getAlias();
		ClassMetadata metadata = commonRepository.getMetadata(permission.getEntity());
		for (QueryFilter fi : permission.getFilters()) {
			fillCondition(metadata.getMappedClass(), alias, filter, fi.toCondition(), paramList);
		}
		return filter.toString();
	}

	private Class<?> getType(Class<?> clazz, String field) {
		int dot = field.indexOf(".");
		if (dot > 0) {
			String subField = field.substring(0, dot);
			Class<?> type = BeanUtils.getPropertyDescriptor(clazz, subField).getPropertyType();
			return getType(type, field.substring(dot + 1));
		}
		return BeanUtils.getPropertyDescriptor(clazz, field).getPropertyType();
	}

	@Override
	public void afterPropertiesSet() throws Exception {
		conversionService.addConverter(new FastJSONConverter(conversionService));
	}

	@RequestMapping("time")
	@ResponseBody
	public long time() {
		return System.currentTimeMillis();
	}

}
